#include "generic_series.hpp"
#include "disassemble.hpp"

string enclosed( const string& statement,
		const char start = '(', const char close = ')' ){

	if( statement.empty() ) return blank;
	else if( statement.at(0) != start ) return blank;

	int count = 1;
	for( unsigned i = 1; i < statement.length(); i++ ){
		if( statement.at( i ) == start ) count++;
		else if( statement .at( i ) == close ){
			if( count == 1 )
				return statement.substr( 1, i-1 );
			else count--;
		}
	}

	return blank;

}

// To avoid any errors, retain the parentheses of functions.
// EXAMPLE: In "fn1( fn2( x, y ), z )", if we want the first
// of fn1 and substitute of fn2, we need the parenthesis to
// correctly recognize the first _parameter_.
bool single_fn_transform( string& s, const RE& fn_pattern, const int& replace ){

	static const regex not_in_delimiter( "\\b(not\\s+in|in)\\s*\\(", regex::icase );
	if( replace == 3 ) s = regex_replace( s, not_in_delimiter, "NOT_IN(" );

	string contents, selected;
	if( !fn_pattern.PartialMatch( s, &selected ) )
		return false;

	// no whitespace is allowed in the
	// function name ('fname') and any
	// word-characters after the first
	// incidence of a space (' ') are
	// simply ignored (as if not there).
	string fname;

	int preceding = 0;
	while( selected.at( preceding ) == ' ' )
		preceding++;
	for( bool stop = false; selected.at( preceding ) != '('; preceding++ ){
		if( selected.at( preceding ) == ' ' ) stop = true;
		else if( !stop ) fname.push_back( selected.at( preceding ) );
	}

	// Ensure that finding the function name didn't involve ignoring junk like " word2_or_crap"
	// from "word1 word2_or_crap()". REMOVED CHECK AS SUCH INSTANCES ARE NOT SUPPLIED (HERE).
	// assert( trim_copy( replace_first_copy( selected.substr( 0, preceding ), fname, "" ) ).empty() );

	to_upper( fname );

	contents = enclosed( selected.substr( preceding ) );
	selected = selected.substr( 0, preceding )+"("+contents+")";
	if( replace == 0 ){
		replace_first( s, selected, " F#U#N#C#T#I#O#N " );
	}
	else if( replace == 1 )
		replace_first( s, selected, " ("+contents+") " );
	else if( replace == 2 ){
		pair<bool,string> first_in_series = first( contents );
		//assert( first_in_series.first );
		replace_first( s, selected, " ("+first_in_series.second+") " );
	}
	else if( replace == 3 )
		replace_first( s, selected, " " );
	else if( replace == 4 ){
		strings words = tokenize( contents, false, false );
		if( !words.empty() ) replace_first( s, selected, " ("+words.at(0)+") " );
		else replace_first( s, selected, " TR#UN#C#AT#ED " );
	}
	else if( replace == 5 ){
		pair<bool,string> last_in_series = last( contents );
		//assert( last_in_series.first );
		replace_first( s, selected, " ("+last_in_series.second+") " );
	}
	// Protect necessary functions, which are
	// parsed differently or later on...
	else if( replace == 6 ){
		// Note the added space before the
		// start of the enclosed portion.
		replace_first( s, selected, " "+selected.substr( 0, preceding )+" ("+contents+") " );
	}
	else if( replace == 7 ){
		strings words = tokenize( contents, false, false );
		if( !words.empty() ) replace_first( s, selected, " ("+words.at( words.size()-1 )+") " );
		else replace_first( s, selected, " TR#UN#C#AT#ED " );
	}
	else if( replace == 8 ){

		// These (below) seem sufficient for the sample log as such,
		// nonetheless, in general, none of these keywords can be
		// used as function names (JUST DOUBLE CHECK ONCE!).
		// static const RE misleading( "(as|from|on|over|when)", icase );
		static const RE misleading( or_ed_keywords, icase );

		if( misleading.FullMatch( fname ) ) // ..since these are not actual functions.
			replace_first( s, selected, selected.substr( 0, preceding )+" ("+contents+")" );
		else{
			if( s.at( selected.length() ) == '\'' ) return false;
			if( unaccounted.count( fname ) > 0 ) unaccounted[ fname ]++;
			else unaccounted.insert( make_pair( fname, 1 ) );
			replace_first( s, selected, "{"+trim_copy( contents )+"}" );
		}

	}
	else if( replace == 9 ){
		// Exceptional case for parsing the "substring"
		// function (see structure in documentation)...
		replace_first( contents, " FOR ", ", " );
		replace_first( contents, " FROM ", ", " );
		pair<bool,string> first_word_series = first( contents );
		//assert( first_word_series.first );
		replace_first( s, selected, " ("+first_word_series.second+") " );
	}
	else assert( replace >= 0 and replace <= 9 );

	return true;

}

inline void recursive_fn_transform( string& s, const RE& fn_pattern, const int& replace ){

	while( single_fn_transform( s, fn_pattern, replace ) );

}
